package org.review.datastructure;

import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import java.util.Objects;
import java.util.Stack;

/**
 * 重点关注temp.next 判断条件的使用场景
 */
public class SingleLinkList {
    HeroNode head = new HeroNode(0, "", "");


    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");


        singleLinkList.addHeroByOrder(hero4);
        singleLinkList.addHeroByOrder(hero2);
        singleLinkList.addHeroByOrder(hero3);
        singleLinkList.addHeroByOrder(hero1);
        singleLinkList.showHero();
        System.out.println("测试添加两个相同no");
        HeroNode hero5 = new HeroNode(1, "宋江", "及时雨");
        singleLinkList.addHeroByOrder(hero5);

        singleLinkList.showHero();
        System.out.println("修改节点后");
        singleLinkList.update(new HeroNode(1, "宋江", "你猜猜"));
        singleLinkList.showHero();

        System.out.println("删除节点=1");
        singleLinkList.delHero(1);
        singleLinkList.showHero();
        System.out.println("节点个数:" + singleLinkList.lenth(singleLinkList.head));

        System.out.println("获取倒数 第4个英雄" + singleLinkList.getHeroByLastIndex(singleLinkList.head, 4));

        singleLinkList.reverseList(singleLinkList.head);
        System.out.println("反转链表后show");
        singleLinkList.showHero();
        System.out.println("倒叙输出链表");
        singleLinkList.reversePrint(singleLinkList.head);

    }


    public void addHero(HeroNode hero) {
        HeroNode temp = head;
        // TODO: 2024/8/1 实现1
     /*   while (Objects.nonNull(temp.next)) {
            temp = temp.next;
        }
        temp.next = hero;*/

        // TODO: 2024/8/1 实现2  和实现1没什么区别 淡出是将退出循环条件放到循环体里边了
    /*    while (true) {
            if (temp.next == null) break;
            temp = temp.next;
        }
        temp.next = hero;*/
    }

    /**
     * 当列表里有大于要插入的节点, 那么就停下来,将目标节点查到此节点前边
     *
     * @param heroNode
     */
    public void addHeroByOrder(HeroNode heroNode) {
        HeroNode temp = head;
        // TODO: 2024/8/1  实现1 直接判断找到位置并替换,如果没有找到也会放到最后
     /*   while (temp.next != null && temp.next.no < heroNode.no) {
            temp = temp.next;
        }
        HeroNode temp1 = temp.next;
        temp.next = heroNode;
        heroNode.next = temp1;*/

        // TODO: 2024/8/1 实现2: 主要不同 使用辅助标记 判断找到的位置,找到就放到前边 没找到就追加到尾部
        boolean flag = false;
        while (true) {
            if (temp.next == null) break;

            if (temp.next.no > heroNode.no) {
                flag = true;
                break;
            }
            // TODO: 2024/8/1 赋值需要放到最后,此时说明没有找到,如果找到了需要将节点插入到此节点前边,要用到他的前一个节点
            temp = temp.next;

        }
        if (flag) {
            HeroNode temp1 = temp.next;
            temp.next = heroNode;
            heroNode.next = temp1;
        } else {
            temp.next = heroNode;
        }
    }

    public void update(HeroNode heroNode) {
        HeroNode temp = head;
        // TODO: 2024/8/1 实现方式1:直接找到退出条件,如果不是空,那么就直接修改
        while (null != temp.next && !temp.next.name.equals(heroNode.name)) {
            temp = temp.next;
        }
        if (temp.next == null) {
            System.out.println("没有此节点");
            return;
        }
        temp.next.no = heroNode.no;
        temp.next.nickName = heroNode.nickName;
    }

    public void delHero(int no) {
        //TODO 实现方式1

       /* HeroNode current = head;
        boolean flag = false;
        while (true) {
            if (current.next == null) break;

            if (current.next.no == no) {
                flag = true;
                break;
            }

            current = current.next;
        }
        if (flag) {
            current.next = current.next.next;
        } else {
            System.out.println("没有找到此元素");
        }*/


        //TODO 实现方式2 逻辑稍微复杂 代码简介
        HeroNode temp = head;
        while (temp.next != null && temp.next.no != no) {
            temp = temp.next;
        }
        if (temp.next == null) {
            System.out.println("没有此节点");
            return;
        }

        temp.next = temp.next.next;
    }

    public void showHero() {
        HeroNode temp = head.next;
        while (Objects.nonNull(temp)) {
            System.out.println(temp);
            temp = temp.next;
        }
    }

    /**
     * 返回单链表的长度
     *
     * @param head
     * @return
     */
    public int lenth(HeroNode head) {
        int len = 0;
        if (head.next == null) return len;
        len = 1;
        HeroNode temp = head.next;

        while (temp.next != null) {
            len++;
            temp = temp.next;
        }
        return len;
    }


    public HeroNode getHeroByLastIndex(HeroNode head, int index) {
        if (head.next == null) return null;
        int len = lenth(head);
        if (index <= 0 || index > len) return null;
        HeroNode temp = head;
        int lenth = len - index;
        while ((lenth-- >= 0) && temp.next != null) {
            temp = temp.next;
        }
        return temp;
    }

    /**
     * @param head
     */
    public void reverseList(HeroNode head) {
        //TODO 这里如果写cur = head,那么也会交换head并倒转为最后一个, 在最后一步head.next 赋值的时候就会变成 倒数第一个元素的next = head; head.next  = 第一个,变成死循环
        HeroNode cur = head.next;
        HeroNode next;
        HeroNode reverse = new HeroNode(0, "", "");
        //todo 这里必须使用cur!=null, 如果使用cur.next!=null倒数最后一个元素的时候,不进行逆转
        while (cur != null) {
            next = cur.next;
            cur.next = reverse.next; //todo 这一步容易混淆  误将reverse.next赋值给cur,此时 相当于白做工,next 赋值给cur ,cur又赋值给 reverse.next
            reverse.next = cur;
            cur = next;
        }
        //TODO 不能写head = reverse,因为 这里的head 是head的一个引用副本,指向相同的地址,但并不是同一个变量
        head.next = reverse.next;
    }

    public void reversePrint(HeroNode head) {
        Stack<HeroNode> heroNodes = new Stack<>();
        if (head.next == null) return;
        HeroNode stemp = head.next;
        while (stemp != null) {
            heroNodes.push(stemp);
            stemp = stemp.next;
        }
        int size = heroNodes.size();
        for (int i = 0; i < size; i++) {
            System.out.println(heroNodes.pop());
        }
    }
}


@RequiredArgsConstructor
class HeroNode {
    @NonNull int no;
    @NonNull String name;
    @NonNull String nickName;
    HeroNode next;

    @Override
    public String toString() {
        return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + ", nickName='" + nickName + '\'' + '}';
    }
}